package com.vhall.component.service.export.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vhall.component.dao.export.ExportMapper;
import com.vhall.component.dao.interactive.QuestionsMapper;
import com.vhall.component.entity.data.vo.DataExportParamVO;
import com.vhall.component.entity.data.vo.DataExportRspVO;
import com.vhall.component.entity.export.ExportEntity;
import com.vhall.component.entity.export.vo.*;
import com.vhall.component.entity.interactive.entity.QuestionsEntity;
import com.vhall.component.entity.interactive.vo.QuestionConsoleExportReqVO;
import com.vhall.component.entity.page.vo.PageVO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.vo.RoomAdminListReqVO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.HttpConstant;
import com.vhall.component.plugin.common.enums.ExportTitleTypeEnum;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.RandomUtil;
import com.vhall.component.service.export.ExportService;
import com.vhall.component.service.export.assembler.ExportAssembler;
import com.vhall.component.service.export.common.ExportTypeRef;
import com.vhall.component.service.export.handler.BaseAdminExportHandler;
import com.vhall.component.service.export.manager.ExportMqManager;
import com.vhall.component.service.export.manager.ExportTypeManager;
import com.vhall.component.service.room.RoomInnerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.connection.stream.StringRecord;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author guoxin.wang
 * @date 2021-06-15 14:01
 */
@Slf4j
@Service
public class ExportServiceImpl implements ExportService {

    @Value("${vhall.export.type:csv}")
    private String exportType;
    @Value("${vhall.export.stream-name:export-stream}")
    private String streamName;
    @Value("${vhall.export.group-name:export-group}")
    private String groupName;
    @Value("${vhall.export.consumer-name:export-consumer}")
    private String consumerName;

    @Autowired
    private ExportMapper exportMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private ExportAssembler exportAssembler;

    @Autowired
    private ExportMqManager exportMqManager;

    @Autowired
    private Map<String, BaseAdminExportHandler> adminExportHandlerMap;
    @Autowired
    private QuestionsMapper questionsMapper;

    @Override
    public ExportPage<ExportListRspVO> exportList(ExportListReqVO reqVO) {
        // 为了兼容之前的老数据
        Integer ilId = null;
        if (StringUtils.isNotBlank(reqVO.getRoomId())) {
            RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
            if (roomInfo == null) {
                throw new BusinessException(BizErrorCode.EMPTY_ROOM);
            }
            ilId = roomInfo.getIlId();
        }
        log.info("进入查询方法，参数为：{}", reqVO);

        List<ExportListRspVO> result = new ArrayList<>();
        LambdaQueryWrapper<ExportEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(reqVO.getExport() != null, ExportEntity::getExport, reqVO.getExport())
                .eq(ilId != null, ExportEntity::getIlId, ilId)
                .eq(reqVO.getAccountId() != null,ExportEntity::getAccountId, reqVO.getAccountId())
                .eq(reqVO.getSourceId() != null, ExportEntity::getSourceId, reqVO.getSourceId())
                .eq(reqVO.getStatus() != null && !"0".equals(reqVO.getStatus()), ExportEntity::getStatus, reqVO.getStatus())
                .ge(reqVO.getBeginTime() != null, ExportEntity::getCreatedAt, reqVO.getBeginTime())
                .le(reqVO.getEndTime() != null, ExportEntity::getCreatedAt, reqVO.getEndTime())
                .orderByDesc(ExportEntity::getCreatedAt);
        Page<ExportEntity> page = new Page<>(reqVO.getPage(), reqVO.getPageSize());
        page = exportMapper.selectPage(page, wrapper);
        List<ExportEntity> records = page.getRecords();
        ExportListRspVO single;
        for (ExportEntity record : records) {
            single = new ExportListRspVO();
            BeanUtils.copyProperties(record, single);
            single.setFileUrl(record.getOssFile());
            result.add(single);
        }
        return new ExportPage<>(reqVO.getPage(), reqVO.getPageSize(), Integer.parseInt(String.valueOf(page.getTotal())), result);
    }

    @Override
    public Boolean listExport(Token token, RoomAdminListReqVO vo) {

        String fileName = "RoomsList" + LocalDateTime.now();
        ExportEntity export = new ExportEntity();
        export.setExport("roomList");
        export.setIlId(0);
        export.setAccountId(token.getAccountId());
        export.setSourceId("");
        export.setFileName(fileName);
        List<String> titles = new ArrayList<>();
        titles.add("直播ID");
        titles.add("直播名称");
        titles.add("直播码");
        titles.add("主持人id");
        titles.add("主持人用户名");
        titles.add("主持人昵称");
        titles.add("直播总时长/秒");
        titles.add("观众总人数/人");
        titles.add("观看总次数/次");
        export.setTitle(JSON.toJSONString(titles));
        export.setParams(JsonUtil.toJsonString(vo));
        export.setCallback("roomService:exportList");
        export.setRoomId("");
        return createExport(export);
    }

    @Override
    public Boolean exportQuestionAnswer(QuestionConsoleExportReqVO reqVO) {
        Token token = TokenThreadLocal.getToken();
        // 2、获取房间信息。
        RoomsEntityDTO liveInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (liveInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        QuestionsEntity questionsEntity = questionsMapper.selectById(reqVO.getQuestionId());
        String fileName = liveInfo.getSubject() + "-" + questionsEntity.getTitle()+ "-" +  DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN) + cn.hutool.core.util.RandomUtil.randomNumbers(4);
        // 3、把数据插入export表。
        ExportEntity ee = new ExportEntity();
        ee.setExport("questionAnswer");
        ee.setAccountId(token.getAccountId());
        ee.setIlId(liveInfo.getIlId());
        ee.setSourceId(reqVO.getQuestionId());
        ee.setFileName(fileName);
        List<String> titles = new ArrayList<>();
        titles.add("直播ID");
        titles.add("直播名称");
        titles.add("问卷ID");
        titles.add("问卷名称");
        titles.add("填写人数");
        titles.add("昵称");
        titles.add("手机号");
        titles.add("客户号");
        ee.setTitle(JSON.toJSONString(titles));
        Map<String, String> param = new HashMap<>();
        param.put("question_id", reqVO.getQuestionId());
        param.put("il_id", liveInfo.getIlId().toString());
        ee.setParams(JSON.toJSONString(param));
        ee.setExt("csv");
        ee.setCallback("questionService:questionAnswerExport");
        ee.setRoomId(reqVO.getRoomId());
        return createExport(ee);
    }

    @Override
    public Boolean createExport(ExportEntity ee) {
        boolean result;
        // 需要对ilid字段进行处理。如果传递的字段为空的话，就设置为0；
        if (null == ee.getIlId()) {
            ee.setIlId(0);
        }
        // 在这里对ext字段做处理。
        ee.setExt(exportType);
        int insert = exportMapper.insert(ee);
        result = insert > 0;
        if (result) {
            // 发送消息
            try {
                StringRecord record = StreamRecords.string(Collections.singletonMap(HttpConstant.DATA, ee.getId().toString()))
                        .withStreamKey(streamName);
                RecordId recordId = stringRedisTemplate.opsForStream().add(record);
                log.info("export表插入数据的消息发送成功,[{}]", recordId);
            } catch (Exception e) {
                log.info("export表插入数据的消息发送成功,[{}]", e.getMessage());
                throw new RuntimeException(StrUtil.format("export表插入数据的消息发送失败,[{}]", e));
            }
        }
        return result;
    }

    @Override
    public void whiteAccountTemplate(HttpServletResponse response) throws Exception {
        //读取文件的路径
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("templates/WhiteAccountsList.xls");
        downloadFile(stream, "WhiteAccountsList.xls", response);
    }

    @Override
    public List<DataExportRspVO> getAllInfo(DataExportParamVO reqVO) {
        return exportMapper.getAllInfo(reqVO);
    }

    @Override
    public Integer getAllInfoNum(DataExportParamVO reqVO) {
        return exportMapper.getAllInfoNum(reqVO);
    }

    @Override
    public void lotteryTemplate(HttpServletResponse response) throws Exception {
        //读取文件的路径
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("templates/LotteryUserList.xlsx");
        downloadFile(stream, "LotteryUserList.xlsx", response);
    }

    @Override
    public void filterTemplate(HttpServletResponse response) throws IOException {
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("templates/FilterWords.xlsx");
        downloadFile(stream, "FilterWords.xlsx", response);
    }

    @Override
    public PageVO<ExportAdmRspVO> exportList(ExportAdmListReqVO reqVO, Integer adminId) {
        reqVO.setDefault();
        Integer type = reqVO.getType();
        LambdaQueryWrapper<ExportEntity> where = Wrappers.lambdaQuery();
        where.eq(ExportEntity::getAdminId, adminId);
        // 按业务类型过滤
        where.eq(Objects.nonNull(type) && type > 0, ExportEntity::getType, type);
        where.orderByDesc(ExportEntity::getId);
        Page<ExportEntity> page = new Page<>(reqVO.getPage(), reqVO.getPageSize());
        page = exportMapper.selectPage(page, where);
        List<ExportAdmRspVO> list = exportAssembler.toRspVos(page.getRecords());
        return new PageVO<>(reqVO.getPage(), reqVO.getPageSize(), (int) page.getTotal(), list);
    }

    @Override
    public Boolean createExport(ExportAdmCreateReqVO reqVO, Integer adminId) {
        ExportEntity entity = new ExportEntity();
        entity.setIlId(0);
        entity.setAdminId(adminId);
        entity.setStatus(1);
        entity.setExt("xlsx");
        ExportTypeRef typeRef = ExportTypeManager.getTypeRef(reqVO.getType());
        entity.setType(typeRef.getType());
        entity.setTitle(typeRef.getTitle());
        entity.setExport(typeRef.getExport());
        entity.setCallback(typeRef.getCallback());
        entity.setParams(" ");
        if (StringUtils.isNotBlank(reqVO.getSearchCondition())) {
            entity.setParams(reqVO.getSearchCondition());
        }
        // 文件名为：文件前缀_yyyyMMdd
        BaseAdminExportHandler adminExportHandler = adminExportHandlerMap.get("adminExportHandler_" + typeRef.getType());
        if (reqVO.getType().equals(1) || reqVO.getType().equals(7)) {
            entity.setFileName("live_room_info");
        } else {
            entity.setFileName(adminExportHandler.getFileName() + "_" + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN));
        }

        int count = adminExportHandler.count(entity);
        if (count > 60000) {
            throw new BusinessException(BizErrorCode.BIZ_EXPORT_LIMIT);
        }

        // 插入成功，采用队列方式处理
        if (exportMapper.insert(entity) > 0) {
            return exportMqManager.sendAdminExportCreateMessage(entity.getId());
        }
        throw new BusinessException(BizErrorCode.BIZ_CREATE_FAILED);
    }

    @Override
    public Boolean reExport(AdmReExportReqVO reqVO) {
        Integer id = reqVO.getId();
        ExportEntity exportEntity = exportMapper.selectById(id);
        if (Objects.isNull(exportEntity)) {
            throw new BusinessException(BizErrorCode.EMPTY_ACTION);
        }
        // 非导出失败状态，不允许操作
        if (exportEntity.getStatus() != 4) {
            throw new BusinessException(BizErrorCode.BIZ_TYPE_NOT_ALLOWED);
        }

        LambdaUpdateWrapper<ExportEntity> wrapper = Wrappers.lambdaUpdate();
        wrapper.set(ExportEntity::getStatus, 1).set(ExportEntity::getUpdatedAt, LocalDateTime.now()).eq(ExportEntity::getId, id)
                .eq(ExportEntity::getStatus, 4);
        if (exportMapper.update(null, wrapper) <= 0) {
            throw new BusinessException(BizErrorCode.EMPTY_ACTION);
        }
        return exportMqManager.sendAdminExportCreateMessage(id);
    }

    @Override
    public String getFileName(String roomName, Integer type, String exportTypeName) {
        StringBuffer sb = new StringBuffer();
        if (roomName.length() > 20) {
            sb.append(roomName, 0, 20).append("-");
        } else {
            sb.append(roomName).append("-");
        }
        switch (type) {
            case 7:
            case 9:
            case 11:
            case 13:
                sb.append(exportTypeName).append("-");
                break;
            default:
                sb.append(ExportTitleTypeEnum.getExportDetails(type)).append("-");
                break;
        }
        SimpleDateFormat sdf = new SimpleDateFormat();
        sdf.applyPattern(DatePattern.PURE_DATE_PATTERN);
        Date date = new Date();
        sb.append(sdf.format(date));
        sb.append(RandomUtil.getRandomNum(1000, 9999));
        return sb.toString();
    }

    private void downloadFile(InputStream is, String name, HttpServletResponse response) throws IOException {
        //设置响应头  "application/octet-stream"
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLDecoder.decode(name, "ISO-8859-1"));
        //输出流自动关闭
        try (OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
                os.flush();
            }
        } finally {
            if (is != null) {
                is.close();
            }
        }
    }

}
